#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <netinet/in.h>
#include <unistd.h>

#include "ptypes.h"
#include "rabinfp.h"
#include "diskcache.h"
#include "packeteer.h"
#include "chunk.h"
#include "cacheindex.h"

/*
  Format of serialized chunk:

  int: size of short fingerprint map for the chunk
  array of (unsigned long: fingerprint, unsigned short: position) tuples
  int: length of this chunk
  int: length of reserved space for IP header
  char[]: chunk payload

*/

bool Chunk::
Serialize(long handle, CacheIndex* index, int newRecordNumber) {
  unsigned char data[sizeof(int) + sizeof(m_length) + 
		     sizeof(m_headerLength) + MAX_FINGERPRINTS*(sizeof(short) + 
								sizeof(Fingerprint)) + MAX_CHUNK_SIZE];

  int size = m_map.GetSize();
  *(int *)data = size;

  // up to MAX_FINGERPRINTS fingerprints 
  int i = 0;
  m_map.Rewind();

Log("Serialize: record number %d\n", newRecordNumber);
  Fingerprint fp;
  short position;
  int written;
  for (; m_map.GetNextFingerprint(&fp, &position); i++) {
Log("    fingerprint %16llx\n", fp);

    *(Fingerprint *)(data + sizeof(size) + i*(sizeof(fp) + sizeof(position))) = fp;
    *(short *)(data + sizeof(size) + i*(sizeof(fp) + sizeof(position)) + sizeof(fp)) = position;

    index->PutFingerprint(fp, newRecordNumber);	

  }


   *(int *)(data + sizeof(size) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position))) = m_length;


  // IP header length
   *(int *)(data + sizeof(size)+ sizeof(m_length) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position))) = m_headerLength;


   memcpy(data + sizeof(size) + sizeof(m_length) + sizeof(m_headerLength) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position)), m_chunk, m_length);


   int ret = m_parent->m_disk.cacheWrite(data, sizeof(size) + sizeof(m_length) + sizeof(m_headerLength) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position)) + m_length);  

  return true;
}

bool Chunk::Deserialize(long handle) {
  // up to 256 fingerprints 
  unsigned char data[sizeof(int) + sizeof(m_length) + 
		     sizeof(m_headerLength) + MAX_FINGERPRINTS*(sizeof(short) + 
								sizeof(Fingerprint)) + MAX_CHUNK_SIZE];


  int ret = m_parent->m_disk.cacheRead(data, sizeof(data));
  assert(ret == sizeof(data));

  int size = *(int *)data;

  if (0 == size) {
    return false;
  }

  assert(size <= MAX_FINGERPRINTS);
 
  int i = 0;

  Fingerprint fp;
  short position;

  for (i=0; i < size; i++) {
    fp = *(Fingerprint *)(data + sizeof(size) + i*(sizeof(fp) + sizeof(position)));
    position = *(short *)(data + sizeof(size) + i*(sizeof(fp) + sizeof(position)) + sizeof(fp));
    m_map.AddFingerprint(fp, position);
  }
 
  m_length = *(int *)(data + sizeof(size) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position)));

  m_chunk= new unsigned char[m_length];


  // IP header length
  m_headerLength = *(int *)(data + sizeof(size)+ sizeof(m_length) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position)));

  memcpy(m_chunk, data + sizeof(size) + sizeof(m_length) + sizeof(m_headerLength) + MAX_FINGERPRINTS*(sizeof(fp) + sizeof(position)), m_length);

  return true;
}

bool Chunk::WarmCacheIndex(long handle) {
#if 0
  // space for number of finterprints and fingerprint array
  unsigned char data[sizeof(int) + 
		     MAX_FINGERPRINTS*(sizeof(short) + sizeof(Fingerprint))];

  extern int Read(int, long, void *, int);
  int ret = m_parent->m_disk.cacheRead(data, sizeof(data));
  assert(ret == sizeof(data));

  int size = *(int *)data;

  assert(size > 0 && size <= MAX_FINGERPRINTS);
 
  int i = 0;

  Fingerprint fp;

  for (i=0; i < size; i++) {
    fp = *(Fingerprint *)(data + sizeof(size) + i*(sizeof(fp) + sizeof(short)));
    m_parent->m_disk.m_index.PutFingerprint(fp, m_recordNumber);
  }
 
#endif
  Fingerprint fp;
  short pos;
  while(GetNextFingerprint(&fp, &pos)) {
    m_parent->m_disk.m_index.PutFingerprint(fp, m_recordNumber);
  }
  return true;
}
